Include the names of your collaborators here.
This homework is dedicated to working with logistic regression for
binary classification. You will explore a binary classification
application and fit non-Bayesian logistic regression models by
maximizing the likelihood via the glm() function R. Then
you will fit Bayesian logistic regression models with the Laplace
Approximation by programming the log-posterior function. You will
identify the best model and make predictions to study the trends in the
predicted event probability. You will conclude the application by tuning
various non-Bayesian models. First you will tune a non-Bayesian logistic
regression model with the elastic net penalty to identify to help
identify the most important features in the model. You will visualize
the predicted event probability trends from the tuned elastic net model
and compare the results with your Bayesian models. Then you tune several
advanced methods like neural networks and random forests. This last
portion of the assignment introduces the basic syntax for training and
tuning those advanced methods with caret. You will focus on
assessing their performance via cross-validation and visualizing their
predictive trends in order to compare their behavior to the simpler
generalized linear models you previously fit!
You will use the tidyverse, coefplot,
broom, MASS, glmnet,
nnet, randomForest, and caret
packages in this assignment. The caret package will prompt
you to install nnet and randomForest if you do
not have them installed already.
IMPORTANT: The RMarkdown assumes you have downloaded the data sets (CSV files) to the same directory you saved the template Rmarkdown file. If you do not have the CSV files in the correct location, the data will not be loaded correctly.
Certain code chunks are created for you. Each code chunk has
eval=FALSE set in the chunk options. You
MUST change it to be eval=TRUE in order
for the code chunks to be evaluated when rendering the document.
You are free to add more code chunks if you would like.
This assignment will use packages from the tidyverse
suite.
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.4.0 ✔ purrr 1.0.1
## ✔ tibble 3.1.8 ✔ dplyr 1.1.0
## ✔ tidyr 1.3.0 ✔ stringr 1.5.0
## ✔ readr 2.1.2 ✔ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
This assignment also uses the broom package. The
broom package is part of tidyverse and so you
have it installed already. The broom package will be used
via the :: operator later in the assignment and so you do
not need to load it directly.
The caret package will be loaded later in the assignment
and the glmnet, nnet, and
randomForest packages will be loaded via
caret.
The primary data set you will work with in this assignment is loaded for you in the code chunk below.
df1 <- readr::read_csv('hw10_binary_01.csv', col_names = TRUE)
## Rows: 225 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): x1
## dbl (3): x2, x3, y
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
The data consists of 3 inputs, x1, x2, and
x3, and one binary outcome, y. The glimpse
below shows that x1 is a character and thus is a
categorical input. The x2 and x3 inputs are
continuous. The binary outcome has been encoded as y=1 for
the EVENT and y=0 for the NON-EVENT.
df1 %>% glimpse()
## Rows: 225
## Columns: 4
## $ x1 <chr> "C", "B", "C", "B", "A", "C", "A", "A", "C", "C", "B", "C", "A", "C…
## $ x2 <dbl> 1.682873632, -1.033648456, 0.110854156, 2.032934019, -0.225540507, …
## $ x3 <dbl> -0.353085685, -0.778102544, 0.757536960, 0.639465847, 0.017483448, …
## $ y <dbl> 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0…
It is always best to explore data before training predictive models. This assignment does not require you to create all figures necessary to sufficiently explore the data. This assignment focuses on various ways of exploring the relationships between the binary outcome and the inputs. You will thus not consider input correlation plots in this assignment. Please note that the inputs have already been standardized for you to streamline the visualizations and modeling. Realistic applications like the final project may have inputs with vastly different scales and so you will need to execute the preprocessing as part of the model training.
The code chunk below reshapes the wide-format df1 data
into long-format, lf1. The continuous inputs,
x1 and x2, are “gathered” and “stacked” on top
of each other. The long-format data supports using facets associated
with the continuous inputs. You will use the long-format data in some of
the visualizations below.
lf1 <- df1 %>%
tibble::rowid_to_column() %>%
pivot_longer(c(x2, x3))
The glimpse below shows the continuous input names are contained in
the name column and their values are contained in the
value column.
lf1 %>% glimpse()
## Rows: 450
## Columns: 5
## $ rowid <int> 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11…
## $ x1 <chr> "C", "C", "B", "B", "C", "C", "B", "B", "A", "A", "C", "C", "A",…
## $ y <dbl> 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0…
## $ name <chr> "x2", "x3", "x2", "x3", "x2", "x3", "x2", "x3", "x2", "x3", "x2"…
## $ value <dbl> 1.682873632, -0.353085685, -1.033648456, -0.778102544, 0.1108541…
Let’s start with exploring the inputs.
Visualize the distributions of the continuous inputs as
histograms in ggplot2.
It is up to you as to whether you use the wide format or long-format data. If you use the wide-format data you should create two separate figures. If you use the long-format data you should use facets for each continuous input.
Add your code chunks here.
lf1 %>% ggplot(aes(x = value)) +
geom_histogram(bins = 30, color = "black", fill = "lightblue") +
facet_wrap(~ name, scales = "free_x")+
theme_minimal()
Visualize the counts of the categorical input x1
with a bar chart in ggplot2. You MUST use the wide-format
data for this visualization.
Add your code chunks here.
df1 %>% ggplot(aes(x = x1)) +
geom_bar(color = "black", fill = "lightblue")
Let’s examine if there are differences in the continuous input distributions based on the categorical input. You will use boxplots to focus on the distribution summary statistics.
You must use the long-format data for this figure. Create a
boxplot in ggplot2 where the x aesthetic is
the categorical input and the y aesthetic is the
value column. You must include facets associated with the
name column.
Add your code chunks here.
lf1 %>% ggplot(aes(x = x1, y = value, fill = x1)) +
geom_boxplot() +
facet_wrap(~ name, scales = "free_y")
Let’s now focus on the binary outcome.
Visualize the counts of the binary outcome y
with a bar chart in ggplot2. You MUST use the wide-format
data for this visualization.
Is the binary outcome balanced?
Add your code chunks here.
df1 %>% ggplot(aes(x = factor(y))) +
geom_bar(color = "black", fill = "lightblue") +
theme_minimal()
What do you think?
According to the image, I think their binary outcomes are not
balanced.
Let’s see if the categorical input impacts the binary outcome.
Create a bar chart for the categorical input x1
with ggplot2 like you did in 1b). However, you must also
map the fill aesthetic to
as.factor(y).
The data type conversion function as.factor() can be
applied in-line. This will force a categorical fill palette to be used
rather than a continuous palette.
Add your code chunks here.
df1 %>% ggplot(aes(x = x1, fill = as.factor(y))) +
geom_bar(position = "dodge") +
theme_minimal()
Let’s now visualize the binary outcome with respect to the continuous
inputs. You will use the geom_jitter() function instead of
the geom_point() function to create the scatter plot. The
geom_jitter() function adds small amounts of random noise
to “jitter” or perturb the locations of the points. This will make it
easier to see the individual observations of the binary outcome. You
MUST use the long-format data for this question.
Pipe the long-format data to ggplot() and map
the x aesthetic to the value variable and the
y aesthetic to the y variable. Add a
geom_jitter() layer where you specify the
height and width arguments to be 0.02 and 0,
respectively. Do NOT set height and width
within the aes() function. Facet by the continuous inputs
by including a facet_wrap() layer with the facets “a
function of” the name column.
Add your code chunks here.
lf1 %>%
ggplot(aes(x = value, y = y, color = as.factor(y))) +
geom_jitter(height = 0.02, width = 0) +
facet_wrap(~ name, scales = "free_x")
We can include a logistic regression smoother to help visualize the
changes in the event probability. You will use the
geom_smooth() function to do so, but you will need to
change the arguments compared to previous assignments that focused on
regression.
Create the same plot as 1f) but include
geom_smooth() as a layer between geom_jitter()
and facet_wrap(). Specify the formula argument
to y ~ x, the method argument to be
glm, and the method.args argument to be
list(family = 'binomial').
The formula argument are “local” variables associated
with the aesthetics. Thus the formula y ~ x means the
y aesthetic is linearly related to the x
aesthetic. However, by specifying method = glm and
method.args = list(family = 'binomial') you are instructing
geom_smooth() to fit a logistic regression model. Thus, you
are actually specifying that the linear predictor, the log-odds
ratio is linearly related to the x aesthetic.
Add your code chunks here.
lf1 %>%
ggplot(aes(x = value, y = y)) +
geom_point() +
geom_jitter(height = 0.02, width = 0, alpha = 0.7) +
geom_smooth(formula = y ~ x, method = "glm",method.args = list(family = 'binomial')) +
facet_wrap(~ name)
Let’s check if the categorical input influences the event probability trends with respect to the continuous inputs.
Create the same figure as in 1g), except this time map the
color and fill aesthetics within the
geom_smooth() layer to the x1 variable. You
must also map the color aesthetic within the
geom_jitter() layer to the x1
variable.
Based on your figure do the trends appear to depend on the categorical input?
Add your code chunks here.
lf1 %>%
ggplot(aes(x = value, y = y)) +
geom_jitter(aes(color = x1), height = 0.02, width = 0, alpha = 0.7) +
geom_smooth(aes(color = x1, fill = x1), formula = y ~ x, method = "glm", method.args = list(family = "binomial")) +
facet_wrap(~ name, scales = "free_x") +
theme_minimal()
What do you think?
I think the event probability trends may indeed depend on the
categorical input.
The previous figures used the “basic” formula of y ~ x
within geom_smooth(). However, we can try more complex
relationships within geom_smooth(). For example, let’s
consider quadratic relationships between the log-odds ratio (linear
predictor) and the continuous inputs.
Create the same figure as 1h), except this time specify the
formula argument to be y ~ x + I(x^2). Use the
same set of aesthetics as 1h) including the color and
fill aesthetics.
Does the quadratic relationship appear to be consistent with the data for either of the 2 inputs?
Add your code chunks here.
lf1 %>%
ggplot(aes(x = value, y = y)) +
geom_jitter(aes(color = x1), height = 0.02, width = 0, alpha = 0.7) +
geom_smooth(aes(color = x1, fill = x1), formula = y ~ x + I(x^2), method = "glm", method.args = list(family = "binomial")) +
facet_wrap(~ name, scales = "free_x") +
theme_minimal()
What do you think?
Yes, they are consistent.
Now that you have explored the data, it’s time to start modeling! You
will fit multiple non-Bayesian logistic regression models using
glm(). These models will range from simple to complex. You
do not need to standardize the continuous inputs, they have already been
standardized for you. You can focus on the fitting the models.
BE CAREFUL!! Make sure you set the
family argument in glm() correctly!!! The
family argument was discussed earlier in the semester.
You must fit the following models:
A: Categorical input only
B: Linear additive features using the continuous inputs only
C: Linear additive features using all inputs (categorical and
continuous)
D: Interact the categorical input with the continuous inputs
E: Add the categorical input to linear main effects and interaction
between the continuous inputs
F: Interact the categorical input with main effect and interaction
features associated with the continuous inputs
G: Add the categorical input to the linear main continuous effects,
interaction between continuous, and quadratic continuous features
H: Interact the categorical input to the linear main continuous effects,
interaction between continuous, and quadratic continuous features
You must name your models modA through
modH.
You do not need to fit all models in a single code chunk.
Add your code chunks here.
modA <- glm(y ~ x1, data = df1, family = "binomial")
modB <- glm(y ~ x2 + x3, data = df1, family = "binomial")
modC <- glm(y ~ x1 + x2 + x3, data = df1, family = "binomial")
modD <- glm(y ~ x1 * (x2 + x3), data = df1, family = "binomial")
modE <- glm(y ~ x1 + x2 * x3, data = df1, family = "binomial")
modF <- glm(y ~ x1 * (x2 * x3), data = df1, family = "binomial")
modG <- glm(y ~ x1 + (x2 * x3 + I(x2^2) + I(x3^2)), data = df1, family = "binomial")
modH <- glm(y ~ x1 * (x2 * x3 + I(x2^2) + I(x3^2)), data = df1, family = "binomial")
modH
##
## Call: glm(formula = y ~ x1 * (x2 * x3 + I(x2^2) + I(x3^2)), family = "binomial",
## data = df1)
##
## Coefficients:
## (Intercept) x1B x1C x2 x3 I(x2^2)
## -2.1614 -1.4239 -0.5378 0.2377 0.1860 -0.1697
## I(x3^2) x2:x3 x1B:x2 x1C:x2 x1B:x3 x1C:x3
## 1.6711 -0.2581 0.7046 4.0907 -0.1625 -1.3616
## x1B:I(x2^2) x1C:I(x2^2) x1B:I(x3^2) x1C:I(x3^2) x1B:x2:x3 x1C:x2:x3
## -0.8564 -0.6776 0.7122 0.3194 -0.2825 2.0044
##
## Degrees of Freedom: 224 Total (i.e. Null); 207 Residual
## Null Deviance: 280.6
## Residual Deviance: 142.2 AIC: 178.2
Which of the 8 models is the best? Which of the 8 models is the second best?
State the performance metric you used to make your selection.
HINT: The broom::glance() function will help
here…
Add your code chunks here.
library(broom)
glance(modA)$AIC
## [1] 279.4618
glance(modB)$AIC
## [1] 270.3974
glance(modC)$AIC
## [1] 265.1775
glance(modD)$AIC
## [1] 254.1712
glance(modE)$AIC
## [1] 267.1745
glance(modF)$AIC
## [1] 253.1065
glance(modG)$AIC
## [1] 190.4613
glance(modH)$AIC
## [1] 178.1737
Because the best model have the lowest AIC, so the best model is modH and second best model is modG.
Create the coefficient plot associated with your best and second best models. How many coefficients are statistically significant in each model?
You should use the coefplot::coefplot() function to
create the plots.
Add your code chunks here.
library(coefplot)
coefplot(modH)
coefplot(modG)
In the best modelH, there are 3 statistically significants.
In the second best model G, there are 4 statistically significants.
Now that you have an idea about the relationships, it’s time to consider a more detailed view of the uncertainty by fitting Bayesian logistic regression models. You defined log-posterior functions for linear models in previous assignments. You worked with simple linear relationships, interactions, polynomials, and more complex spline basis features. In lecture, we discussed how the linear model framework can be generalized to handle non-continuous binary outcomes. The likelihood changed from a Gaussian to a Binomial distribution and a non-linear link function is required. In this way, the regression model is applied to a linear predictor which “behaves” like the trend in an ordinary linear model. In this problem, you will define the log-posterior function for logistic regression. By doing so you will be able to directly contrast what you did to define the log-posterior function for the linear model in previous assignments.
The complete probability model for logistic regression consists of the likelihood of the response \(y_n\) given the event probability \(\mu_n\), the inverse link function between the probability of the event, \(\mu_n\), and the linear predictor, \(\eta_n\), and the prior on all linear predictor model coefficients \(\boldsymbol{\beta}\).
As in lecture, you will assume that the \(\boldsymbol{\beta}\)-parameters are a-priori independent Gaussians with a shared prior mean \(\mu_{\beta}\) and a shared prior standard deviation \(\tau_{\beta}\).
Write out complete probability model for logistic regression. You must write out the \(n\)-th observation’s linear predictor using the inner product of the \(n\)-th row of a design matrix \(\mathbf{x}_{n,:}\) and the unknown \(\boldsymbol{\beta}\)-parameter column vector. You can assume that the number of unknown coefficients is equal to \(D + 1\).
You are allowed to separate each equation into its own equation block.
HINT: The “given” sign, the vertical line, \(\mid\), is created by typing
\mid in a latex math expression. The product symbol (the
giant PI) is created with prod_{}^{}.
Add your equation blocks here. \[ P \left( y_n | \mu_n \right) = \mu_n^\left( y_n \right) \left( 1 - \mu_n \right)^ \left( 1- y_n \right) \] \[ \mu_n = logit^\left( -1 \right) \left( -η_n \right) \]
\[ η_n = X_n,:\beta \]
You will fit 8 Bayesian logistic regression models using the same linear predictor trend expressions that you used in the non-Bayesian logistic regression models. You will program the log-posterior function in the same style as the linear model log-posterior functions. This allows you to use the same Laplace Approximation strategy to execute the Bayesian inference.
You must first define the design matrices for each of the 8
models. You must name the design matrices Xmat_A through
Xmat_H. As a reminder, the 8 models are provided
below.
A: Categorical input only
B: Linear additive features using the continuous inputs only
C: Linear additive features using all inputs (categorical and
continuous)
D: Interact the categorical input with the continuous inputs
E: Add the categorical input to linear main effects and interaction
between the continuous inputs
F: Interact the categorical input with main effect and interaction
features associated with the continuous inputs
G: Add the categorical input to the linear main continuous effects,
interaction between continuous, and quadratic continuous features
H: Interact the categorical input to the linear main continuous effects,
interaction between continuous, and quadratic continuous features
Create the design matrices for the 8 models. Add your code chunks below.
Xmat_A <- model.matrix(~ x1, data = df1)
Xmat_B <- model.matrix(~ x2 + x3, data = df1)
Xmat_C <- model.matrix(~ x1 + x2 + x3, data = df1)
Xmat_D <- model.matrix(~ x1 * (x2 + x3), data = df1)
Xmat_E <- model.matrix(~ x1 + x2 * x3, data = df1)
Xmat_F <- model.matrix(~ x1 * (x2 * x3), data = df1)
Xmat_G <- model.matrix(~ x1 + (x2 * x3 + I(x2^2) + I(x3^2)), data = df1)
Xmat_H <- model.matrix(~ x1 * (x2 * x3 + I(x2^2) + I(x3^2)), data = df1)
The log-posterior function you will program requires the design
matrix, the observed output vector, and the prior specification. In
previous assignments, you provided that information with a list. You
will do the same thing in this assignment. The code chunk below is
started for you. The lists follow the same naming convention as the
design matrices. The info_A list corresponds to the
information for model A, while info_H corresponds to the
information for model H. You must assign the design matrix to the
corresponding list and complete the rest of the required information.
The observed binary outcome vector must be assigned to the
yobs field. The prior mean and prior standard deviation
must be assigned to the mu_beta and tau_beta
fields, respectively.
Complete the code chunk below by completing the lists of required for each model. The list names are consistent with the design matrix names you defined in the previous problem. You must use a prior mean of 0 and a prior standard deviation of 4.5.
info_A <- list(
yobs = df1$y,
design_matrix = Xmat_A,
mu_beta = 0,
tau_beta = 4.5
)
info_B <- list(
yobs = df1$y,
design_matrix = Xmat_B,
mu_beta = 0,
tau_beta = 4.5
)
info_C <- list(
yobs = df1$y,
design_matrix = Xmat_C,
mu_beta = 0,
tau_beta = 4.5
)
info_D <- list(
yobs = df1$y,
design_matrix = Xmat_D,
mu_beta = 0,
tau_beta = 4.5
)
info_E <- list(
yobs = df1$y,
design_matrix = Xmat_E,
mu_beta = 0,
tau_beta = 4.5
)
info_F <- list(
yobs = df1$y,
design_matrix = Xmat_F,
mu_beta = 0,
tau_beta = 4.5
)
info_G <- list(
yobs = df1$y,
design_matrix = Xmat_G,
mu_beta = 0,
tau_beta = 4.5
)
info_H <- list(
yobs = df1$y,
design_matrix = Xmat_H,
mu_beta = 0,
tau_beta = 4.5
)
You will now define the log-posterior function for logistic
regression, logistic_logpost(). The first argument to
logistic_logpost() is the vector of unknowns and the second
argument is the list of required information. You will assume that the
variables within the my_info list are those contained in
the info_A through info_H lists you defined
previously.
Complete the code chunk to define the
logistic_logpost() function. The comments describe what you
need to fill in. Do you need to separate out the \(\boldsymbol{\beta}\)-parameters from the
vector of unknowns?
After you complete logistic_logpost(), test it
by setting the unknowns vector to be a vector of -1’s and
then 2’s for the model A case (the model with a only the categorical
input). If you have successfully programmed the function you should get
-164.6906 and -541.6987 for the -1 test case
and +2 test case, respectively.
Do you need to separate the \(\boldsymbol{\beta}\)-parameters from the
unknowns vector?
No, I don’t need to separate the \(\boldsymbol{\beta}\)-parameters from the
unknowns vector.
logistic_logpost <- function(unknowns, my_info)
{
# extract the design matrix and assign to X
X <- my_info$design_matrix
# calculate the linear predictor
eta <- X %*% unknowns
# calculate the event probability
mu <- boot::inv.logit(eta)
# evaluate the log-likelihood
log_lik <- sum(my_info$yobs * log(mu) + (1 - my_info$yobs) * log(1 - mu))
# evaluate the log-prior
log_prior <- sum(dnorm(unknowns, mean = my_info$mu_beta, sd = my_info$tau_beta, log = TRUE))
# sum together
return(log_lik + log_prior)
}
Test out your function using the info_A information and
setting the unknowns to a vector of -1’s.
###
logistic_logpost(rep(-1, ncol(Xmat_A)), info_A)
## [1] -164.6906
Test out your function using the info_A information and
setting the unknowns to a vector of 2’s.
###
logistic_logpost(rep(2, ncol(Xmat_A)), info_A)
## [1] -541.6987
The my_laplace() function is provided to you in the code
chunk below.
my_laplace <- function(start_guess, logpost_func, ...)
{
# code adapted from the `LearnBayes`` function `laplace()`
fit <- optim(start_guess,
logpost_func,
gr = NULL,
...,
method = "BFGS",
hessian = TRUE,
control = list(fnscale = -1, maxit = 5001))
mode <- fit$par
post_var_matrix <- -solve(fit$hessian)
p <- length(mode)
int <- p/2 * log(2 * pi) + 0.5 * log(det(post_var_matrix)) + logpost_func(mode, ...)
# package all of the results into a list
list(mode = mode,
var_matrix = post_var_matrix,
log_evidence = int,
converge = ifelse(fit$convergence == 0,
"YES",
"NO"),
iter_counts = as.numeric(fit$counts[1]))
}
You will use my_laplace() to execute the Laplace
Approximation for all 8 models. You must use an initial guess of zero
for all unknowns for each model.
Perform the Laplace Approximation for all 8 models. Assign
the results to the laplace_A through laplace_H
objects. The names should be consistent with the design matrices and
lists of required information. Thus, laplace_A must
correspond to the info_A and Xmat_A
objects.
Should you be concerned that the initial guess will impact the results?
Does the initial guess matter?
Yes. According to Laplace results, the initial guess will impact the
results.
Add the code chunks here.
laplace_A <- my_laplace(rep(0, ncol(Xmat_A)), logistic_logpost, info_A)
laplace_A
## $mode
## [1] -0.55500941 -0.84527527 0.04408551
##
## $var_matrix
## [,1] [,2] [,3]
## [1,] 0.05782617 -0.05757377 -0.05767426
## [2,] -0.05757377 0.14570798 0.05742253
## [3,] -0.05767426 0.05742253 0.11071731
##
## $log_evidence
## [1] -145.3736
##
## $converge
## [1] "YES"
##
## $iter_counts
## [1] 18
laplace_B <- my_laplace(rep(0, ncol(Xmat_B)), logistic_logpost, info_B)
laplace_B
## $mode
## [1] -0.8778061 0.5823639 -0.1492538
##
## $var_matrix
## [,1] [,2] [,3]
## [1,] 0.023897929 -0.006483630 0.001546943
## [2,] -0.006483630 0.024109873 -0.001991444
## [3,] 0.001546943 -0.001991444 0.022148352
##
## $log_evidence
## [1] -142.4161
##
## $converge
## [1] "YES"
##
## $iter_counts
## [1] 35
laplace_C <- my_laplace(rep(0, ncol(Xmat_C)), logistic_logpost, info_C)
laplace_C
## $mode
## [1] -0.7130612 -0.9261202 0.1926824 0.6475370 -0.1832697
##
## $var_matrix
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0.0665238839 -0.061381852 -0.0657344204 -0.008569389 0.0007614299
## [2,] -0.0613818522 0.161514564 0.0617490669 -0.006868123 0.0047607662
## [3,] -0.0657344204 0.061749067 0.1220206295 0.006626974 -0.0002383668
## [4,] -0.0085693895 -0.006868123 0.0066269738 0.027199560 -0.0028668110
## [5,] 0.0007614299 0.004760766 -0.0002383668 -0.002866811 0.0235298623
##
## $log_evidence
## [1] -142.8198
##
## $converge
## [1] "YES"
##
## $iter_counts
## [1] 25
laplace_D <- my_laplace(rep(0, ncol(Xmat_D)), logistic_logpost, info_D)
laplace_D
## $mode
## [1] -0.6420239 -0.9059754 -0.0853263 0.4026794 -0.1013667 -0.2991826 1.6410969
## [8] -0.4699179 -0.2012216
##
## $var_matrix
## [,1] [,2] [,3] [,4] [,5]
## [1,] 6.461174e-02 -6.424703e-02 -6.428363e-02 -0.0164466867 1.037720e-05
## [2,] -6.424703e-02 1.747682e-01 6.392092e-02 0.0163143200 8.208691e-05
## [3,] -6.428363e-02 6.392092e-02 1.541192e-01 0.0162218156 1.444089e-05
## [4,] -1.644669e-02 1.631432e-02 1.622182e-02 0.0615372131 -7.719003e-04
## [5,] 1.037720e-05 8.208691e-05 1.444089e-05 -0.0007719003 4.499597e-02
## [6,] 1.634376e-02 -2.961592e-02 -1.611994e-02 -0.0613011689 7.640379e-04
## [7,] 1.608822e-02 -1.595859e-02 -6.563896e-02 -0.0607478742 6.481087e-04
## [8,] 1.234753e-04 4.114239e-02 -1.474668e-04 0.0007273425 -4.475768e-02
## [9,] 6.434099e-05 -1.557677e-04 1.020623e-02 0.0006027858 -4.474318e-02
## [,6] [,7] [,8] [,9]
## [1,] 0.0163437645 0.0160882190 0.0001234753 6.434099e-05
## [2,] -0.0296159234 -0.0159585905 0.0411423917 -1.557677e-04
## [3,] -0.0161199358 -0.0656389646 -0.0001474668 1.020623e-02
## [4,] -0.0613011689 -0.0607478742 0.0007273425 6.027858e-04
## [5,] 0.0007640379 0.0006481087 -0.0447576845 -4.474318e-02
## [6,] 0.1351301346 0.0605149682 -0.0029845015 -5.956198e-04
## [7,] 0.0605149682 0.3057683029 -0.0006050266 -5.196492e-02
## [8,] -0.0029845015 -0.0006050266 0.1517121534 4.450640e-02
## [9,] -0.0005956198 -0.0519649193 0.0445063996 1.573744e-01
##
## $log_evidence
## [1] -142.795
##
## $converge
## [1] "YES"
##
## $iter_counts
## [1] 45
laplace_E <- my_laplace(rep(0, ncol(Xmat_E)), logistic_logpost, info_E)
laplace_E
## $mode
## [1] -0.713096877 -0.927277305 0.190826126 0.648567377 -0.187250968
## [6] 0.008418843
##
## $var_matrix
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0.066658870 -0.0612855134 -0.065475022 -0.008794406 0.001694410
## [2,] -0.061285513 0.1615041800 0.061803268 -0.006961476 0.005057761
## [3,] -0.065475022 0.0618032684 0.122397329 0.006291223 0.001068750
## [4,] -0.008794406 -0.0069614759 0.006291223 0.027503104 -0.004118232
## [5,] 0.001694410 0.0050577607 0.001068750 -0.004118232 0.028327668
## [6,] -0.001952447 -0.0007104004 -0.003097788 0.002765858 -0.011017949
## [,6]
## [1,] -0.0019524473
## [2,] -0.0007104004
## [3,] -0.0030977883
## [4,] 0.0027658582
## [5,] -0.0110179492
## [6,] 0.0252604119
##
## $log_evidence
## [1] -146.1622
##
## $converge
## [1] "YES"
##
## $iter_counts
## [1] 30
laplace_F <- my_laplace(rep(0, ncol(Xmat_F)), logistic_logpost, info_F)
laplace_F
## $mode
## [1] -0.64054553 -0.93744671 -0.45540006 0.39455027 -0.06102485 -0.06639433
## [7] -0.40619290 2.06065821 -0.49547660 -0.56563937 -0.30992586 1.49133467
##
## $var_matrix
## [,1] [,2] [,3] [,4] [,5]
## [1,] 6.477776e-02 -0.0644196802 -0.0641691500 -0.016657694 -5.969345e-05
## [2,] -6.441968e-02 0.1778392920 0.0638148157 0.016578954 1.560934e-04
## [3,] -6.416915e-02 0.0638148157 0.2157735007 0.016053981 5.504831e-04
## [4,] -1.665769e-02 0.0165789539 0.0160539807 0.062773924 -3.740788e-03
## [5,] -5.969345e-05 0.0001560934 0.0005504831 -0.003740788 6.333726e-02
## [6,] -1.378483e-03 0.0013594571 0.0008631379 0.005750213 -2.902344e-02
## [7,] 1.660234e-02 -0.0111016599 -0.0160012309 -0.062507379 3.746632e-03
## [8,] 1.587919e-02 -0.0158053324 -0.1510459350 -0.061329526 2.866238e-03
## [9,] 1.717206e-04 0.0397542520 -0.0006592057 0.003745951 -6.299027e-02
## [10,] 4.429181e-04 -0.0005364082 0.0787322386 0.003154390 -6.260898e-02
## [11,] 1.412589e-03 0.0177517052 -0.0008995948 -0.005662040 2.890388e-02
## [12,] 6.681455e-04 -0.0006536842 -0.1516025892 -0.004739626 2.797305e-02
## [,6] [,7] [,8] [,9] [,10]
## [1,] -0.0013784834 0.016602339 0.015879194 0.0001717206 0.0004429181
## [2,] 0.0013594571 -0.011101660 -0.015805332 0.0397542520 -0.0005364082
## [3,] 0.0008631379 -0.016001231 -0.151045935 -0.0006592057 0.0787322386
## [4,] 0.0057502126 -0.062507379 -0.061329526 0.0037459512 0.0031543902
## [5,] -0.0290234419 0.003746632 0.002866238 -0.0629902746 -0.0626089847
## [6,] 0.0471101796 -0.005693297 -0.004864354 0.0288720484 0.0284672458
## [7,] -0.0056932966 0.148432005 0.061069232 0.0149566955 -0.0031626587
## [8,] -0.0048643542 0.061069232 0.459341029 -0.0028763508 -0.1497496191
## [9,] 0.0288720484 0.014956695 -0.002876351 0.1765503789 0.0622662067
## [10,] 0.0284672458 -0.003162659 -0.149749619 0.0622662067 0.2346278324
## [11,] -0.0469033866 0.031234728 0.004780985 -0.0247668853 -0.0283503579
## [12,] -0.0458072572 0.004687500 0.241122192 -0.0278277834 -0.1397768992
## [,11] [,12]
## [1,] 0.0014125891 0.0006681455
## [2,] 0.0177517052 -0.0006536842
## [3,] -0.0008995948 -0.1516025892
## [4,] -0.0056620399 -0.0047396261
## [5,] 0.0289038793 0.0279730544
## [6,] -0.0469033866 -0.0458072572
## [7,] 0.0312347284 0.0046874997
## [8,] 0.0047809849 0.2411221919
## [9,] -0.0247668853 -0.0278277834
## [10,] -0.0283503579 -0.1397768992
## [11,] 0.1354739225 0.0456065767
## [12,] 0.0456065767 0.5020176184
##
## $log_evidence
## [1] -146.7974
##
## $converge
## [1] "YES"
##
## $iter_counts
## [1] 43
laplace_G <- my_laplace(rep(0, ncol(Xmat_G)), logistic_logpost, info_G)
laplace_G
## $mode
## [1] -2.24211646 -1.17013199 0.73250335 1.23041793 -0.17999923 -0.37616045
## [7] 1.65803983 -0.07557434
##
## $var_matrix
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0.189500164 -0.074525593 -0.133985873 -0.039890787 -0.0019834901
## [2,] -0.074525593 0.279354476 0.100403368 -0.019048514 0.0121451878
## [3,] -0.133985873 0.100403368 0.196973236 0.019499636 0.0034951884
## [4,] -0.039890787 -0.019048514 0.019499636 0.091855978 -0.0119084454
## [5,] -0.001983490 0.012145188 0.003495188 -0.011908445 0.0449336866
## [6,] -0.009614663 -0.008941243 -0.003350858 -0.035151199 0.0033799776
## [7,] -0.066990332 -0.028884337 0.022213878 0.041566237 -0.0001574764
## [8,] 0.001315817 0.011489333 0.002031697 0.007928597 -0.0005461659
## [,6] [,7] [,8]
## [1,] -0.009614663 -0.0669903322 0.0013158167
## [2,] -0.008941243 -0.0288843373 0.0114893328
## [3,] -0.003350858 0.0222138775 0.0020316974
## [4,] -0.035151199 0.0415662369 0.0079285973
## [5,] 0.003379978 -0.0001574764 -0.0005461659
## [6,] 0.044173814 -0.0137683238 -0.0111489788
## [7,] -0.013768324 0.0758728159 -0.0065776527
## [8,] -0.011148979 -0.0065776527 0.0529434439
##
## $log_evidence
## [1] -110.3315
##
## $converge
## [1] "YES"
##
## $iter_counts
## [1] 34
laplace_H <- my_laplace(rep(0, ncol(Xmat_H)), logistic_logpost, info_H)
laplace_H
## $mode
## [1] -2.1454168 -1.3572839 -0.3840456 0.2739202 0.1653047 -0.1850853
## [7] 1.6594146 -0.2328253 0.6285875 3.5960152 -0.1503919 -1.1915626
## [13] -0.8180670 -0.4238694 0.6598131 0.2329400 -0.2978803 1.6272778
##
## $var_matrix
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 0.28737137 -0.27263595 -0.27893291 -0.02585323 -0.03539902 -0.069279941
## [2,] -0.27263595 1.03889567 0.26470577 0.02270512 0.03337041 0.067024582
## [3,] -0.27893291 0.26470577 0.72977802 0.02048907 0.03614957 0.069174393
## [4,] -0.02585323 0.02270512 0.02048907 0.16108623 -0.02787346 -0.025040656
## [5,] -0.03539902 0.03337041 0.03614957 -0.02787346 0.14409083 0.020745549
## [6,] -0.06927994 0.06702458 0.06917439 -0.02504066 0.02074555 0.105356404
## [7,] -0.14670026 0.13647392 0.14091557 0.02113032 0.02522426 -0.006277128
## [8,] 0.02443811 -0.02262122 -0.02642823 0.03284255 -0.01425265 -0.032223910
## [9,] 0.02083832 -0.24469533 -0.01571868 -0.15780851 0.02850188 0.024860539
## [10,] 0.01875867 -0.01615226 -0.49729607 -0.14693281 0.02323773 0.019960945
## [11,] 0.03379769 -0.08805567 -0.03460809 0.02845795 -0.14286437 -0.020651147
## [12,] 0.03697682 -0.03481538 0.10524898 0.02338160 -0.14111742 -0.018950525
## [13,] 0.07021116 -0.04326855 -0.07002623 0.02379584 -0.02078890 -0.104011040
## [14,] 0.06815144 -0.06585782 -0.01897086 0.01787390 -0.01749690 -0.100188160
## [15,] 0.13714669 -0.58320208 -0.13172870 -0.01830684 -0.02380910 0.006682197
## [16,] 0.14005509 -0.13025434 -0.41271612 -0.01758563 -0.02538661 0.006192685
## [17,] -0.02345570 0.07084851 0.02546432 -0.03280202 0.01421120 0.032115470
## [18,] -0.02506884 0.02315705 -0.21244945 -0.02326502 0.01078729 0.027195922
## [,7] [,8] [,9] [,10] [,11] [,12]
## [1,] -0.146700260 0.02443811 0.02083832 0.01875867 0.033797688 0.03697682
## [2,] 0.136473919 -0.02262122 -0.24469533 -0.01615226 -0.088055675 -0.03481538
## [3,] 0.140915574 -0.02642823 -0.01571868 -0.49729607 -0.034608091 0.10524898
## [4,] 0.021130323 0.03284255 -0.15780851 -0.14693281 0.028457953 0.02338160
## [5,] 0.025224258 -0.01425265 0.02850188 0.02323773 -0.142864365 -0.14111742
## [6,] -0.006277128 -0.03222391 0.02486054 0.01996095 -0.020651147 -0.01895052
## [7,] 0.185413437 -0.03622093 -0.01700234 -0.01495715 -0.023905053 -0.02673585
## [8,] -0.036220934 0.17614692 -0.03284389 -0.02365978 0.014284551 0.01170923
## [9,] -0.017002341 -0.03284389 0.49885620 0.14404991 0.046856167 -0.02411828
## [10,] -0.014957152 -0.02365978 0.14404991 1.46302948 -0.023750245 -0.41979505
## [11,] -0.023905053 0.01428455 0.04685617 -0.02375024 0.307707032 0.13988408
## [12,] -0.026735853 0.01170923 -0.02411828 -0.41979505 0.139884078 0.47878167
## [13,] 0.004225943 0.03243871 -0.12777295 -0.01888634 -0.001968352 0.01904491
## [14,] 0.005397174 0.02581418 -0.01782654 -0.65090521 0.017389740 0.23037481
## [15,] -0.177601725 0.03507784 0.23266188 0.01258802 0.094355903 0.02520446
## [16,] -0.179675805 0.03575655 0.01365397 0.34035130 0.024117744 -0.05546027
## [17,] 0.035206617 -0.17477740 0.03188224 0.02363991 0.007118521 -0.01167208
## [18,] 0.036179130 -0.16289845 0.02344801 0.80905063 -0.010790506 -0.19634657
## [,13] [,14] [,15] [,16] [,17]
## [1,] 0.070211156 0.068151436 0.137146687 0.140055087 -0.023455702
## [2,] -0.043268549 -0.065857823 -0.583202081 -0.130254336 0.070848506
## [3,] -0.070026232 -0.018970861 -0.131728695 -0.412716119 0.025464323
## [4,] 0.023795844 0.017873895 -0.018306842 -0.017585634 -0.032802017
## [5,] -0.020788904 -0.017496895 -0.023809098 -0.025386611 0.014211203
## [6,] -0.104011040 -0.100188160 0.006682197 0.006192685 0.032115470
## [7,] 0.004225943 0.005397174 -0.177601725 -0.179675805 0.035206617
## [8,] 0.032438711 0.025814182 0.035077841 0.035756553 -0.174777401
## [9,] -0.127772945 -0.017826538 0.232661884 0.013653966 0.031882240
## [10,] -0.018886340 -0.650905208 0.012588023 0.340351302 0.023639911
## [11,] -0.001968352 0.017389740 0.094355903 0.024117744 0.007118521
## [12,] 0.019044913 0.230374813 0.025204462 -0.055460275 -0.011672082
## [13,] 0.362694884 0.098932170 -0.175520953 -0.004218263 0.018980162
## [14,] 0.098932170 0.768286727 -0.005891811 -0.094952875 -0.025738215
## [15,] -0.175520953 -0.005891811 0.613983859 0.172178699 -0.077172811
## [16,] -0.004218263 -0.094952875 0.172178699 0.547119152 -0.034774618
## [17,] 0.018980162 -0.025738215 -0.077172811 -0.034774618 0.326471545
## [18,] -0.027487483 -0.524459282 -0.034917593 -0.057910610 0.161610795
## [,18]
## [1,] -0.02506884
## [2,] 0.02315705
## [3,] -0.21244945
## [4,] -0.02326502
## [5,] 0.01078729
## [6,] 0.02719592
## [7,] 0.03617913
## [8,] -0.16289845
## [9,] 0.02344801
## [10,] 0.80905063
## [11,] -0.01079051
## [12,] -0.19634657
## [13,] -0.02748748
## [14,] -0.52445928
## [15,] -0.03491759
## [16,] -0.05791061
## [17,] 0.16161080
## [18,] 1.44752403
##
## $log_evidence
## [1] -112.7092
##
## $converge
## [1] "YES"
##
## $iter_counts
## [1] 54
The laplace_A object is the Bayesian version of
modA that you fit previously in Problem 2a). Let’s compare
the Bayesian result to the non-Bayesian result.
Calculate the 95% confidence interval on the regression
coefficients associated with laplace_A and dispaly the
interval bounds to the screen. Which features are statistically
significant according to the Bayesian model? Are the results consistent
with the non-Bayesian model, modA?
Add your code chunks here.
c(laplace_A $mode[1] - 2 * sqrt(diag(laplace_A $var_matrix)[1]),
laplace_A $mode[1] + 2 * sqrt(diag(laplace_A $var_matrix)[1]))
## [1] -1.03595084 -0.07406797
c(laplace_A $mode[2] - 2 * sqrt(diag(laplace_A $var_matrix)[2]),
laplace_A $mode[2] + 2 * sqrt(diag(laplace_A $var_matrix)[2]))
## [1] -1.60870957 -0.08184096
c(laplace_A $mode[3] - 2 * sqrt(diag(laplace_A $var_matrix)[3]),
laplace_A $mode[3] + 2 * sqrt(diag(laplace_A $var_matrix)[3]))
## [1] -0.6213987 0.7095697
The third feature(x1C) is statistically significant.
summary(modA)
##
## Call:
## glm(formula = y ~ x1, family = "binomial", data = df1)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -0.9695 -0.9528 -0.6628 1.4006 1.8020
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.55431 0.24148 -2.295 0.0217 *
## x1B -0.84968 0.38378 -2.214 0.0268 *
## x1C 0.04349 0.33414 0.130 0.8965
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 280.56 on 224 degrees of freedom
## Residual deviance: 273.46 on 222 degrees of freedom
## AIC: 279.46
##
## Number of Fisher Scoring iterations: 4
Yes, the result is same.
You trained 8 Bayesian logistic regression models. Let’s identify the best using the Evidence based approach!
Calculate the posterior model weight associated with each of
the 8 models. Create a bar chart that shows the posterior model weight
for each model. The models should be named 'A' through
'H'.
Add your code chunks here.
log_evidence <- c(laplace_A$log_evidence, laplace_B$log_evidence, laplace_C$log_evidence, laplace_D$log_evidence, laplace_E$log_evidence, laplace_F$log_evidence, laplace_G$log_evidence, laplace_H$log_evidence)
post_model_weight_unnormalized <- exp(log_evidence - max(log_evidence))
post_model_weight <- post_model_weight_unnormalized / sum(post_model_weight_unnormalized)
post_model_weight
## [1] 5.532077e-16 1.064887e-14 7.111529e-15 7.290534e-15 2.514279e-16
## [6] 1.332109e-16 9.151129e-01 8.488709e-02
model_names <- c('A','B','C','D','E','F','G','H')
df_post_model_weights <- data.frame(Model = model_names, Weight = post_model_weight)
ggplot(df_post_model_weights, aes(x = Model, y = Weight)) +
geom_bar(stat = "identity") +
theme_minimal()
Is your Bayesian identified best model consistent with the non-Bayesian identified best model?
What do you think?
Yes, they have the same result. The best model is G and the second best
model is H.
You trained multiple models ranging from simple to complex. You
identified the best model using several approaches. It is now time to
examine the predictive trends of the models to better interpret their
behavior. You will not predict the training set to study the trends.
Instead, you will visualize the trends on a specifically designed
prediction grid. The code chunk below defines that grid for you using
the expand.grid() function. If you look closely, the
x3 variable has 51 evenly spaced points between -3 and 3.
The x1 variable has 9 unique values evenly spaced between
-3 and 3. These lower and upper bounds are roughly consistent with the
training set bounds. The x1 variable consists of the 3
unique values present in the training set. The
expand.grid() function creates the full-factorial
combination of the 3 variables.
viz_grid <- expand.grid(x1 = unique(df1$x1),
x2 = seq(-3, 3, length.out = 9),
x3 = seq(-3, 3, length.out = 51),
KEEP.OUT.ATTRS = FALSE,
stringsAsFactors = FALSE) %>%
as.data.frame() %>% tibble::as_tibble()
viz_grid %>% glimpse()
## Rows: 1,377
## Columns: 3
## $ x1 <chr> "C", "B", "A", "C", "B", "A", "C", "B", "A", "C", "B", "A", "C", "B…
## $ x2 <dbl> -3.00, -3.00, -3.00, -2.25, -2.25, -2.25, -1.50, -1.50, -1.50, -0.7…
## $ x3 <dbl> -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,…
The glimpse provided above shows there are 1377 combinations of the 3 inputs. You will therefore make over 1300 predictions to study the trends of the event probability!
As with previous linear model assignments, you must first generate
random posterior samples of the unknown parameters from the Laplace
Approximation assumed Multivariate Normal (MVN) distribution. Although
you were able to apply the my_laplace() function to both
the regression and logistic regression settings, you cannot directly
apply the generate_lm_post_samples() function from your
previous assignments. You will therefore adapt
generate_lm_post_samples() and define
generate_glm_post_samples(). The code chunk below starts
the function for you and uses just two input arguments,
mvn_result and num_samples. You must complete
the function.
Why can you not directly use the
generate_lm_post_samples() function? Since the
length_beta argument is NOT provided to
generate_glm_post_samples(), how can you determine the
number of \(\boldsymbol{\beta}\)-parameters? Complete
the code chunk below by first assigning the number of \(\boldsymbol{\beta}\)-parameters to the
length_beta variable. Then generate the random samples from
the MVN distribution. You do not have to name the variables, you only
need to call the correct random number generator.
What do you think? Why do we need a new function compared to the previous assignments?
generate_glm_post_samples <- function(mvn_result, num_samples)
{
# specify the number of unknown beta parameters
length_beta <- length(mvn_result$mode)
# generate the random samples
beta_samples <- MASS::mvrnorm(n = num_samples, mu = mvn_result$mode, Sigma = mvn_result$var_matrix)
# change the data type and name
beta_samples %>%
as.data.frame() %>% tibble::as_tibble() %>%
purrr::set_names(sprintf("beta_%02d", (1:length_beta) - 1))
}
You will now define a function which calculates the posterior samples
on the linear predictor and the event probability. The function,
post_logistic_pred_samples() is started for you in the code
chunk below. It consists of two input arguments Xnew and
Bmat. Xnew is a test design matrix where rows
correspond to prediction points. The matrix Bmat stores the
posterior samples on the \(\boldsymbol{\beta}\)-parameters, where each
row is a posterior sample and each column is a parameter.
Complete the code chunk below by using matrix math to calculate the posterior samples of the linear predictor. Then, calculate the posterior smaples of the event probability.
The eta_mat and mu_mat matrices are
returned within a list, similar to how the Umat and
Ymat matrices were returned for the regression
problems.
HINT: The boot::inv.logit() can take a matrix
as an input. When it does, it returns a matrix as a result.
post_logistic_pred_samples <- function(Xnew, Bmat)
{
# calculate the linear predictor at all prediction points and posterior samples
eta_mat <- Xnew %*% t(Bmat)
# calculate the event probability
mu_mat <-boot::inv.logit(eta_mat)
# book keeping
list(eta_mat = eta_mat, mu_mat = mu_mat)
}
The code chunk below defines a function
summarize_logistic_pred_from_laplace() which manages the
actions necessary to summarize posterior predictions of the event
probability. The first argument, mvn_result, is the Laplace
Approximation object. The second object is the test design matrix,
Xtest, and the third argument, num_samples, is
the number of posterior samples to make. You must follow the comments
within the function in order to generate posterior prediction samples of
the linear predictor and the event probability, and then to summarize
the posterior predictions of the event probability.
The result from summarize_logistic_pred_from_laplace()
summarizes the posterior predicted event probability with the posterior
mean, as well as the 0.05 and 0.95 Quantiles. If you have completed the
post_logistic_pred_samples() function correctly, the
dimensions of the mu_mat matrix should be consistent with
those from the Umat matrix from the regression
problems.
The posterior summary statistics summarize the posterior samples. You
must therefore choose between colMeans() and
rowMeans() as to how to calculate the posterior mean event
probability for each prediction point. The posterior Quantiles are
calculated for you.
Follow the comments in the code chunk below to complete the
definition of the summarize_logistic_pred_from_laplace()
function. You must generate posterior samples, make posterior
predictions, and then summarize the posterior predictions of the event
probability.
HINT: The result from
post_logistic_pred_samples() is a list.
summarize_logistic_pred_from_laplace <- function(mvn_result, Xtest, num_samples)
{
# generate posterior samples of the beta parameters
betas <- generate_glm_post_samples(mvn_result, num_samples)
# data type conversion
betas <- as.matrix(betas)
# make posterior predictions on the test set
pred_test <- post_logistic_pred_samples(Xtest, betas)
# calculate summary statistics on the posterior predicted probability
# summarize over the posterior samples
# posterior mean, should you summarize along rows (rowMeans) or
# summarize down columns (colMeans) ???
mu_avg <- pred_test$mu_mat %>% rowMeans()
# posterior quantiles
mu_q05 <- apply(pred_test$mu_mat, 1, stats::quantile, probs = 0.05)
mu_q95 <- apply(pred_test$mu_mat, 1, stats::quantile, probs = 0.95)
# book keeping
tibble::tibble(
mu_avg = mu_avg,
mu_q05 = mu_q05,
mu_q95 = mu_q95
) %>%
tibble::rowid_to_column("pred_id")
}
You will not make predictions from all 8 models that you previously trained. Instead, you will focus on model D, model G, and model H.
You must define the vizualization grid design matrices
consistent with the model D, model G, and model H formulas. You must
name the design matrices Xviz_D, Xviz_G, and
Xviz_H. You must create the design matrices using the
viz_grid dataframe which was defined at the start of
Problem 04.
Add your code chunks here.
Xviz_D <- model.matrix(~ x1 * (x2 + x3), data = viz_grid)
Xviz_G <- model.matrix(~ x1 + ((x2 * x3) + I(x2^2) + I(x3^2)), data = viz_grid)
Xviz_H <- model.matrix(~ x1 * (x2 * x3 + I(x2^2) + I(x3^2)), data = viz_grid)
Summarize the posterior predicted event probability associated with the three models on the visualization grid. After making the predictions, a code chunk is provided for you which generates a figure showing how the posterior predicted probability summaries compare with the observed binary outcomes. Which of the three models appear to better capture the trends in the binary outcome?
Call summarize_logistic_pred_from_laplace() for
the all three models on the visualization grid. The object names specify
which model you should make predictions with. For example,
post_pred_summary_D corresponds to the predictions
associated with model D. Specify the number of posterior samples to be
2500. Print the dimensions of the resulting objects to the screen. How
many rows are in each data set?
The prediction summarizes should be executed in the code chunk below.
set.seed(8123)
post_pred_summary_D <- summarize_logistic_pred_from_laplace(laplace_D, Xviz_D, 2500)
post_pred_summary_G <- summarize_logistic_pred_from_laplace(laplace_G, Xviz_G, 2500)
post_pred_summary_H <- summarize_logistic_pred_from_laplace(laplace_H, Xviz_H, 2500)
Print the dimensions of the objects to the screen.
###
dim(post_pred_summary_D)
## [1] 1377 4
dim(post_pred_summary_G)
## [1] 1377 4
dim(post_pred_summary_H)
## [1] 1377 4
The code chunk below defines a function for you. The function creates
a figure which visualizes the posterior predictive summary statistics of
the event probability for a single model. The figure is created to focus
on the trend with respect to x3. Facets are used to examine
the influence of x2. The line color and ribbon aesthetics
are used to denote the categorical variable x1. This figure
is specific to the three variable names in this assignment, but it shows
the basic layout required for visualizing predictive trends from
Bayesian logistic regression models with 3 inputs.
viz_bayes_logpost_preds <- function(post_pred_summary, input_df)
{
post_pred_summary %>%
left_join(input_df %>% tibble::rowid_to_column('pred_id'),
by = 'pred_id') %>%
ggplot(mapping = aes(x = x3)) +
geom_ribbon(mapping = aes(ymin = mu_q05,
ymax = mu_q95,
group = interaction(x1, x2),
fill = x1),
alpha = 0.25) +
geom_line(mapping = aes(y = mu_avg,
group = interaction(x1, x2),
color = x1),
size = 1.15) +
facet_wrap( ~ x2, labeller = 'label_both') +
labs(y = "event probability") +
theme_bw()
}
Use the viz_bayes_logpost_preds() function to
visualize posterior predictive trends of the event probability for the 3
models: model D, model G, and model H.
Add your code chunks here.
viz_bayes_logpost_preds(post_pred_summary_D, viz_grid)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
viz_bayes_logpost_preds(post_pred_summary_G, viz_grid)
viz_bayes_logpost_preds(post_pred_summary_H, viz_grid)
Describe the differences in the predictive trends between the 3 models?
What do you think?
Among the three groups of models, model G has the most stable trend and the highest degree of fit.
You should have noticed a pattern associated with the 8 models that you previously fit. The most complex model, model H, contains all other models! It is a super set of all features from the simpler models. An alternative approach to training many models of varying complexity is to train a single complex model and use regularization to “turn off” the unimportant features. This way we can find out if the most complex model can be turned into a simpler model of just the most key features we need!
We discussed in lecture how the Lasso penalty or its Bayesian analog
the Double-Exponential prior are capable of turning off the unimportant
features. We focused on regression problems but Lasso can also be
applied to classification problems! In this problem you will use
caret to manage the training, assessment, and tuning of the
glmnet elastic net penalized logistic regression model. The
code chunk below imports the caret package for you.
library(caret)
## Loading required package: lattice
##
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
##
## lift
The caret package prefers the binary outcome to be
organized as a factor data type compared to an integer. The data set is
reformatted for you in the code chunk below. The binary outcome
y is converted to a new variable outcome with
values 'event' and 'non_event'. The first
level is forced to be 'event' to be consistent with the
caret preferred structure.
df_caret <- df1 %>%
mutate(outcome = ifelse(y == 1, 'event', 'non_event')) %>%
mutate(outcome = factor(outcome, levels = c("event", "non_event"))) %>%
select(x1, x2, x3, outcome)
df_caret %>% glimpse()
## Rows: 225
## Columns: 4
## $ x1 <chr> "C", "B", "C", "B", "A", "C", "A", "A", "C", "C", "B", "C", "A…
## $ x2 <dbl> 1.682873632, -1.033648456, 0.110854156, 2.032934019, -0.225540…
## $ x3 <dbl> -0.353085685, -0.778102544, 0.757536960, 0.639465847, 0.017483…
## $ outcome <fct> event, non_event, non_event, non_event, non_event, event, even…
You must specify the resampling scheme that caret will use to train,
assess, and tune a model. You used caret in the previous
assignment for a regression problem. Here, you are working with a
classification problem and so you cannot use the same performance metric
as the previous assignment! Although there are multiple classification
metrics we could consider, we will focus on Accuracy in this
problem.
Specify the resampling scheme to be 10 fold with 3 repeats.
Assign the result of the trainControl() function to the
my_ctrl object. Specify the primary performance metric to
be 'Accuracy' and assign that to the my_metric
object.
Add your code chunks here.
my_ctrl <- trainControl(method = "repeatedcv", number = 10, repeats = 3)
my_metric <- "Accuracy"
You must train, assess, and tune an elastic model using the default
caret tuning grid. In the caret::train()
function you must use the formula interface to specify a model
consistent with model H. Thus, your model should interact the
categorical input to the linear main continuous effects, interaction
between continuous, and quadratic continuous features. However, please
pay close attention to your formula. The binary outcome is now named
outcome and not y. Assign the
method argument to 'glmnet' and set the metric argument to
my_metric. Even though the inputs were standardized for
you, you must also instruct caret to
standardize the features by setting the preProcess argument
equal to c('center', 'scale'). This will give you practice
standardizing inputs. Assign the trControl argument to the
my_ctrl object.
Important: The caret::train() function
works with the formula interface. Thus, even though you are using
glmnet to fit the model, caret does not
require you to organize the design matrix as required by
glmnet! Thus, you do NOT need to remove
the intercept when defining the formula to
caret::train()!
Train, assess, and tune the glmnet elastic net
model consistent with model H with the defined resampling scheme. Assign
the result to the enet_default object and display the
result to the screen.
Which tuning parameter combinations are considered to be the best?
Is the best set of tuning parameters more consistent with Lasso or Ridge regression?
The random seed is set for you for reproducibility.
set.seed(1234)
enet_default <- train(outcome ~ x1 * ((x2 * x3) + I(x2^2) + I(x3^2)),
data = df_caret,
method = "glmnet",
metric = my_metric,
preProcess = c("center", "scale"),
trControl = my_ctrl)
enet_default
## glmnet
##
## 225 samples
## 3 predictor
## 2 classes: 'event', 'non_event'
##
## Pre-processing: centered (17), scaled (17)
## Resampling: Cross-Validated (10 fold, repeated 3 times)
## Summary of sample sizes: 202, 202, 202, 203, 202, 202, ...
## Resampling results across tuning parameters:
##
## alpha lambda Accuracy Kappa
## 0.10 0.0004769061 0.8251372 0.5805807
## 0.10 0.0047690605 0.8282993 0.5802313
## 0.10 0.0476906052 0.8072793 0.4935810
## 0.55 0.0004769061 0.8251372 0.5805807
## 0.55 0.0047690605 0.8298748 0.5824205
## 0.55 0.0476906052 0.8042545 0.4773741
## 1.00 0.0004769061 0.8207235 0.5714822
## 1.00 0.0047690605 0.8328393 0.5904347
## 1.00 0.0476906052 0.8147892 0.5081578
##
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were alpha = 1 and lambda = 0.004769061.
x1C:I(x3^2) is the best. It same as Lasso.
Create a custom tuning grid to further tune the elastic net
lambda and alpha tuning parameters.
Create a tuning grid with the expand.grid()
function which has two columns named alpha and
lambda. The alpha variable should be evenly
spaced between 0.1 and 1.0 by increments of 0.1. The lambda
variable should have 25 evenly spaced values in the log-space between
the minimum and maximum lambda values from the caret
default tuning grid. Assign the tuning grid to the
enet_grid object.
How many tuning parameter combinations are you trying out? How many total models will be fit assuming the 5-fold with 3-repeat resampling approach?
HINT: The seq() function includes an argument
by to specify the increment width.
Add your code chunks here.
alpha_values <- seq(0.1, 1.0, by = 0.1)
lambda_min <- min(enet_default$results$lambda)
lambda_max <- max(enet_default$results$lambda)
lambda_values <- exp(seq(log(lambda_min), log(lambda_max), length.out = 25))
enet_grid <- expand.grid(alpha = alpha_values, lambda = lambda_values)
nrow(enet_grid)
## [1] 250
total_models <- 3 * 5 * nrow(enet_grid)
total_models
## [1] 3750
How many?
250 models and 3750 parameters
Train, assess, and tune the elastic net model with the custom
tuning grid and assign the result to the enet_tune object.
You should specify the arguments to caret::train()
consistent with your solution in Problem 5b), except you should also
assign enet_grid to the tuneGrid
argument.
Do not print the result to the screen. Instead use the
default plot method to visualize the resampling results. Assign the
xTrans argument to log in the default plot
method call. Use the $bestTune field to print the
identified best tuning parameter values to the screen. Is the identified
best elastic net model more similar to Lasso or Ridge
regression?
The random seed is set for you for reproducibility. You may add more code chunks if you like.
set.seed(1234)
enet_tune <- caret::train(
outcome ~ x1 * ((x2 * x3) + I(x2^2) + I(x3^2)),
data = df_caret,
method = "glmnet",
metric = my_metric,
preProcess = c("center", "scale"),
trControl = my_ctrl,
tuneGrid = enet_grid
)
plot(enet_tune, xTrans = log)
enet_tune$bestTune
## alpha lambda
## 67 0.3 0.01027463
What do you think?
Yes. It is more similar.
Print the coefficients to the screen for the tuned elastic
net model. Which coefficients are non-zero? Has the complex model H been
converted to a simpler model?
#### SOLUTION
### add more code chunks if you'd like
coefs <- coef(enet_tune$finalModel, s = enet_tune$bestTune$lambda)
coefs
## 18 x 1 sparse Matrix of class "dgCMatrix"
## s1
## (Intercept) 1.10820920
## x1B 0.41747015
## x1C .
## x2 -0.34048219
## x3 .
## I(x2^2) 0.18667087
## I(x3^2) -1.72475624
## x2:x3 0.05732699
## x1B:x2 -0.08156494
## x1C:x2 -1.18811574
## x1B:x3 0.03232367
## x1C:x3 0.29324198
## x1B:I(x2^2) 0.43908555
## x1C:I(x2^2) .
## x1B:I(x3^2) -0.28249893
## x1C:I(x3^2) -0.13713286
## x1B:x2:x3 0.19009508
## x1C:x2:x3 -0.25635626
What do you think?
x1C,x3,x1C:I(x2^2) are non-zero
Let’s now visualize the predictions of the event probability from the
tuned elastic net penalized logistic regression model. All
caret trained models make predictions with a
predict() function. The first argument is the
caret trained object and the second object,
newdata, is the new data set to make predictions with.
Earlier in the semester in homework 03, you made predictions from
caret trained binary classifiers. That assignment discussed
that the optional third argument type dictated the “type”
of prediction to make. Setting type = 'prob' instructs the
predict() function to return the class predicted
probabilities.
Complete the code chunk below. You must make predictions on
the visualization grid, viz_grid, using the tuned elastic
net model enet_tune. Instruct the predict()
function to return the probabilities by setting
type = 'prob'.
pred_viz_enet_probs <- predict(enet_tune, newdata = viz_grid, type = 'prob')
The code chunk below is completed for you. The
pred_viz_enet_probs dataframe is column binded to the
viz_grid dataframe. The new object,
viz_enet_df, provides the class predicted probabilities for
each input combination in the visualization grid. A glimpse is printed
to the screen. Please not the eval flag is set to
eval=FALSE in the code chunk below. You must change
eval to eval=TRUE in the chunk options to make
sure the code chunk below runs when you knit the markdown.
viz_enet_df <- viz_grid %>% bind_cols(pred_viz_enet_probs)
viz_enet_df %>% glimpse()
## Rows: 1,377
## Columns: 5
## $ x1 <chr> "C", "B", "A", "C", "B", "A", "C", "B", "A", "C", "B", "A", …
## $ x2 <dbl> -3.00, -3.00, -3.00, -2.25, -2.25, -2.25, -1.50, -1.50, -1.5…
## $ x3 <dbl> -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, …
## $ event <dbl> 0.9999022, 0.8864276, 0.9984171, 0.9999666, 0.9958424, 0.999…
## $ non_event <dbl> 9.779124e-05, 1.135724e-01, 1.582902e-03, 3.336859e-05, 4.15…
The glimpse reveals that the event column stores the
predicted event probability. You must visualize the
predicted event probability in a manner consistent with the
viz_bayes_logpost_preds() function. The caret
trained object does not return uncertainty estimates from the
glmnet model and so you will not include uncertainty
intervals as ribbons. You will visualize the predicted probability as a
line (curve) with respect to x3, for each combination of
x2 and x1.
Pipe the viz_enet_df object to
ggplot(). Map the x aesthetic to the
x3 variable and the y aesthetic to the
event variable. Add a geom_line() layer and
map the color aesthetic to the x1 variable.
Manually assign the size to 1.2. Create the facets by
including the facet_wrap() function and specify the facets
are “functions of” the x2 input.
Add your code chunk here.
viz_enet_df %>%
ggplot(aes(x = x3, y = event, color = x1)) +
geom_line(size = 1.2) +
facet_wrap(~x2)
Are the predicted trends from the tuned elastic net model consistent with the behavior visualized by the Bayesian models?
What do you think?
Use the caret varImp() function to
generate the variable importances associated with the tuned elastic net
model. Plot the variable importances via the default plot
method.
What is the most important feature?
Add your code chunk here.
enet_varimp <- varImp(enet_tune)
plot(enet_varimp)
Let’s now train and tune several advanced methods. You will not
program these methods from scratch in this assignment. Instead, you will
use the caret::train() function to manage the
preprocessing, training, and evaluation of the models via
resampling.
You will use the default caret tuning grids associated
with each of the models. The default tuning may not yield the best
possible performance for these models. For example, small neural
networks are trained in the default grid to make sure the run time is
relatively fast. However, the point is for you to gain experience with
the syntax associated with these models to support your work on the
final project.
You will begin by training a neural network via the nnet
package. caret will prompt you to install nnet
if you do not have it installed already. Please open the R Console to
“see” the prompt messages to help with the installation.
You will train a neural network to classify the binary outcome,
outcome, with respect to all inputs. You should not
interact inputs together. The formula should therefore “look” as if you
are using linear additive features. The neural network will attempt to
create non-linear relationships for you! Assign the method
argument to 'nnet' and set the metric argument
to my_metric. You must also instruct caret to
standardize the features by setting the preProcess argument
equal to c('center', 'scale'). Assign the
trControl argument to the my_ctrl object.
You are therefore using the same resampling scheme for the neural network as you did with the elastic net model! This will allow directly comparing the neural network performance to the elastic net model!
Train, assess, and tune the nnet neural network
with the defined resampling scheme. Assign the result to the
nnet_default object and print the result to the screen.
Which tuning parameter combinations are considered to be the
best?
IMPORTANT: include the argument
trace = FALSE in the caret::train() function
call. This will make sure the nnet package does
NOT print the optimization iteration results to the
screen.
The random seed is set for you for reproducibility. You may add more code chunks if you like.
set.seed(1234)
nnet_default <- train(outcome ~ ., data = df_caret, method = "nnet", trControl = my_ctrl, preProcess = c('center', 'scale'), metric = my_metric, trace = FALSE)
nnet_default
## Neural Network
##
## 225 samples
## 3 predictor
## 2 classes: 'event', 'non_event'
##
## Pre-processing: centered (4), scaled (4)
## Resampling: Cross-Validated (10 fold, repeated 3 times)
## Summary of sample sizes: 202, 202, 202, 203, 202, 202, ...
## Resampling results across tuning parameters:
##
## size decay Accuracy Kappa
## 1 0e+00 0.6886913 0.1707863
## 1 1e-04 0.6855402 0.1692201
## 1 1e-01 0.7019379 0.2065714
## 3 0e+00 0.7585913 0.4005057
## 3 1e-04 0.7630105 0.4113777
## 3 1e-01 0.8014822 0.5105946
## 5 0e+00 0.7612374 0.4241012
## 5 1e-04 0.7746596 0.4677505
## 5 1e-01 0.8176932 0.5557965
##
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were size = 5 and decay = 0.1.
Let’s use predictions to understand the behavior of the neural
network! Predictions are made consistent with the previously trained
elastic net model because caret managed the training of the
neural network. Thus, you will use syntax very similar to the syntax
used to make predictions from the tuned elastic net model.
Complete the code chunk below. You must make predictions on
the visualization grid, viz_grid, using the trained neural
network, nnet_default. Instruct the predict()
function to return the probabilities by setting
type = 'prob'.
pred_viz_nnet_probs <- predict(nnet_default, newdata = viz_grid, type = "prob")
The code chunk below is completed for you. The
pred_viz_nnet_probs dataframe is column binded to the
viz_grid dataframe. The new object,
viz_nnet_df, provides the class predicted probabilities for
each input combination in the visualization grid. A glimpse is printed
to the screen. Please not the eval flag is set to
eval=FALSE in the code chunk below. You must change
eval to eval=TRUE in the chunk options to make
sure the code chunk below runs when you knit the markdown.
viz_nnet_df <- viz_grid %>% bind_cols(pred_viz_nnet_probs)
viz_nnet_df %>% glimpse()
## Rows: 1,377
## Columns: 5
## $ x1 <chr> "C", "B", "A", "C", "B", "A", "C", "B", "A", "C", "B", "A", …
## $ x2 <dbl> -3.00, -3.00, -3.00, -2.25, -2.25, -2.25, -1.50, -1.50, -1.5…
## $ x3 <dbl> -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, …
## $ event <dbl> 0.8247354, 0.2403616, 0.3114790, 0.7902554, 0.3992162, 0.516…
## $ non_event <dbl> 0.17526457, 0.75963841, 0.68852102, 0.20974462, 0.60078381, …
The glimpse reveals that the event column stores the
predicted event probability. You must visualize the
predicted event probability in a manner consistent with the
viz_bayes_logpost_preds() function and the tuned elastic
net model predictions. You will visualize the predicted probability as a
line (curve) with respect to x3, for each combination of
x2 and x1.
Pipe the viz_nnet_df object to
ggplot(). Map the x aesthetic to the
x3 variable and the y aesthetic to the
event variable. Add a geom_line() layer and
map the color aesthetic to the x1 variable.
Manually assign the size to 1.2. Create the facets by
including the facet_wrap() function and specify the facets
are “functions of” the x2 input.
Add your code chunk here.
viz_nnet_df %>% ggplot(aes(x = x3, y = event, color = x1)) +
geom_line(size = 1.2) +
facet_wrap(~x2)
Let’s now a tree based method. You will use the default tuning grid
and thus do not need to specify tuneGrid. Tree based models
do not have the same kind of preprocessing requirements as other models.
Thus, you do not need the preProcess argument in the
caret::train() function call. We will discuss why that is
the case in lecture.
Train a random forest binary classifier by setting the
method argument equal to "rf". You must set
importance = TRUE in the caret::train()
function call. You should not define any interactions or derive features
from the inputs in the formula interface. The formula interface should
“look” like linear additive features. Assign the result to the
rf_default variable. Display the rf_default
object to the screen.
IMPORTANT: caret will prompt you in the
R Console to install the randomForest package if you do not
have it. Follow the instructions.
The random seed is set for you for reproducibility. You may add more code chunks if you like.
PLEASE NOTE: This code chunk may take several minutes to complete!
set.seed(1234)
rf_default <- train(outcome ~ ., data = df_caret, method = "rf", metric = my_metric, importance = TRUE, trControl = my_ctrl)
rf_default
## Random Forest
##
## 225 samples
## 3 predictor
## 2 classes: 'event', 'non_event'
##
## No pre-processing
## Resampling: Cross-Validated (10 fold, repeated 3 times)
## Summary of sample sizes: 202, 202, 202, 203, 202, 202, ...
## Resampling results across tuning parameters:
##
## mtry Accuracy Kappa
## 2 0.8074769 0.5274879
## 3 0.7925889 0.4992796
## 4 0.7927811 0.5032971
##
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 2.
Let’s examine the random forest behavior through predictions.
Complete the code chunk below. You must make predictions on
the visualization grid, viz_grid, using the random forest
model
rf_default``. Instruct thepredict()function to return the probabilities by settingtype
= ‘prob’`.
pred_viz_rf_probs <- predict(rf_default, viz_grid, type = 'prob')
The code chunk below is completed for you. The
pred_viz_rf_probs dataframe is column binded to the
viz_grid dataframe. The new object, viz_rf_df,
provides the class predicted probabilities for each input combination in
the visualization grid according to the random forest model. A glimpse
is printed to the screen. Please not the eval flag is set
to eval=FALSE in the code chunk below. You must change
eval to eval=TRUE in the chunk options to make
sure the code chunk below runs when you knit the markdown.
viz_rf_df <- viz_grid %>% bind_cols(pred_viz_rf_probs)
viz_rf_df %>% glimpse()
## Rows: 1,377
## Columns: 5
## $ x1 <chr> "C", "B", "A", "C", "B", "A", "C", "B", "A", "C", "B", "A", …
## $ x2 <dbl> -3.00, -3.00, -3.00, -2.25, -2.25, -2.25, -1.50, -1.50, -1.5…
## $ x3 <dbl> -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, …
## $ event <dbl> 0.490, 0.404, 0.440, 0.490, 0.404, 0.440, 0.492, 0.406, 0.44…
## $ non_event <dbl> 0.510, 0.596, 0.560, 0.510, 0.596, 0.560, 0.508, 0.594, 0.55…
The glimpse reveals that the event column stores the
predicted event probability. You must visualize the
predicted event probability in the same fashion as you did in 6c).
Pipe the viz_rf_df object to
ggplot(). Map the x aesthetic to the
x3 variable and the y aesthetic to the
event variable. Add a geom_line() layer and
map the color aesthetic to the x1 variable.
Manually assign the size to 1.2. Create the facets by
including the facet_wrap() function and specify the facets
are “functions of” the x2 input.
Add your code chunks here.
viz_rf_df %>%
ggplot(mapping = aes(x = x3, y = event, color = x1)) +
geom_line(size = 1.2) +
facet_wrap(~x2,)
You should have included importance = TRUE in the
caret::train() call in 6d). This allows the random forest
specific variable importance rankings to be returned.
Create a plot to show the variable importance rankings associated with the random forest model. Are the importance rankings consistent with the rankings from the elastic net model?
Add your code chunks here.
plot(varImp(rf_default))
Lastly, let’s compare the various caret trained models
based on our resampling scheme.
Complete the first code chunk below which compiles the defaul elastic net, tuned elastic net, default neural network, and the default random forest models together.
The field names in the list state which model should be assigned.
caret_acc_compare <- resamples(list(ENET_default = enet_default,
ENET_tune = enet_tune,
NNET_default = nnet_default,
RF_default = rf_default))
Visually compare the models based on the resampled Accuracy with a dotplot.
Use the dotplot() function to visualize the
resampled performance summary for each model. Assign the
metric argument to 'Accuracy' to force the
dotplot() function to only show Accuracy.
Which model is the best for this application?
Add your code chunks here.
dotplot(caret_acc_compare, metric = "Accuracy")
How would you describe the differences in the predictions between the 3 types of models you trained in this application?
What do you think?